home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / telecomm / sticpsrc.lzh / SOURCE.ARC / SMTPSERV.C < prev    next >
C/C++ Source or Header  |  1990-07-10  |  20KB  |  901 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  *  enhanced 4/88 Dave Trulli nn2z
  3.  */
  4. #include <stdio.h>
  5. #include <time.h>
  6. #ifdef UNIX
  7. #include <sys/types.h>
  8. #endif
  9. #include <ctype.h>
  10. #include "global.h"
  11. #include "mbuf.h"
  12. #include "netuser.h"
  13. #include "timer.h"
  14. #include "tcp.h"
  15. #include "smtp.h"
  16.  
  17. char *getname();
  18. void mail_delete();
  19. static int rqueuejob();
  20. int queuejob();
  21. int validate_address();
  22. long get_msgid();
  23. struct list *addlist();
  24. struct list *expandalias();
  25. char *getenv(),*getnenv();
  26.  
  27. /* Command table */
  28. static char *commands[] = {
  29.     "helo",
  30. #define HELO_CMD    0
  31.     "noop",
  32. #define NOOP_CMD    1
  33.     "mail from:",
  34. #define MAIL_CMD    2
  35.     "quit",
  36. #define QUIT_CMD    3
  37.     "rcpt to:",
  38. #define RCPT_CMD    4
  39.     "help",
  40. #define HELP_CMD    5
  41.     "data",
  42. #define DATA_CMD    6
  43.     "rset",
  44. #define RSET_CMD    7
  45.     NULLCHAR
  46. };
  47.  
  48. /* Reply messages */
  49. static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
  50. static char banner[] = "220 %s SMTP ready\r\n";
  51. static char closing[] = "221 Closing\r\n";
  52. static char ok[] = "250 Ok\r\n";
  53. static char reset[] = "250 Reset state\r\n";
  54. static char sent[] = "250 Sent\r\n";
  55. static char ourname[] = "250 %s, Share and Enjoy!\r\n";
  56. static char enter[] = "354 Enter mail, end with .\r\n";
  57. static char rcpterr[] = "452 Out of memory for recipients\r\n";
  58. static char ioerr[] = "452 Temp file write error\r\n";
  59. static char mboxerr[] = "452 Mailbox write error\r\n";
  60. static char badcmd[] = "500 Command unrecognized\r\n";
  61. static char syntax[] = "501 Syntax error\r\n";
  62. static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
  63. static char unknown[] = "550 <%s> address unknown\r\n";
  64.  
  65. static char txtname[] = "%s%s.txt";
  66. static char wrkname[] = "%s%s.wrk";
  67. extern char mailsuf[];
  68.  
  69. static struct tcb *smtp_tcb;
  70. /* Start up SMTP receiver service */
  71. smtp1(argc,argv)
  72. int argc;
  73. char *argv[];
  74. {
  75.     struct socket lsocket;
  76.     void r_mail(),s_mail();
  77.     char tos = 0;
  78.  
  79.     lsocket.address = ip_addr;
  80.     if(argc < 2)
  81.         lsocket.port = SMTP_PORT;
  82.     else {
  83.         if ((lsocket.port = atoi(argv[1])) == 0)
  84.             lsocket.port = SMTP_PORT;
  85.         tos = get_tos(argv[2]);
  86.     }
  87.  
  88.     smtp_tcb = open_tcp(&lsocket,NULLSOCK,
  89.         TCP_SERVER,0,r_mail,NULLVFP,s_mail,tos,(char *)NULL);
  90.     return 0;
  91. }
  92.  
  93. /* Shutdown SMTP service (existing connections are allowed to finish) */
  94. smtp0()
  95. {
  96.     if(smtp_tcb != NULLTCB)
  97.         close_tcp(smtp_tcb);
  98.     return 0;
  99. }
  100.  
  101. /* SMTP connection state change upcall handler */
  102. static void
  103. s_mail(tcb,old,new)
  104. struct tcb *tcb;
  105. char old,new;
  106. {
  107.     struct mail *mail_create();
  108.  
  109.     switch(new){
  110. #ifdef    QUICKSTART
  111.     case SYN_RECEIVED:
  112. #else
  113.     case ESTABLISHED:
  114. #endif
  115.         if(mail_create(tcb) == NULLMAIL){
  116.             close_tcp(tcb);
  117.             break;
  118.         }
  119.         tprintf(tcb,banner,hostname);
  120.         start_timer(&tcb->timeout);    /* don't wait forever */
  121.         log_tcp(tcb,"open SMTP");
  122.         break;
  123.     case CLOSE_WAIT:
  124.         close_tcp(tcb);
  125.         break;
  126.     case CLOSED:
  127.         if(tcb != smtp_tcb)
  128.             log_tcp(tcb,"close SMTP");
  129.         mail_delete((struct mail *)tcb->user);
  130.         del_tcp(tcb);
  131.         /* Check if server is being shut down */
  132.         if(tcb == smtp_tcb)
  133.             smtp_tcb = NULLTCB;
  134.         break;
  135.     }
  136. }
  137.  
  138. /* SMTP receiver upcall handler */
  139. static void
  140. r_mail(tcb,cnt)
  141. struct tcb *tcb;
  142. int16 cnt;
  143. {
  144.     register struct mail *mp;
  145.     char c;
  146.     struct mbuf *bp;
  147.     void docommand(),doline();
  148.  
  149.     if((mp = (struct mail *)tcb->user) == NULLMAIL){
  150.         /* Unknown session */
  151.         close_tcp(tcb);
  152.         return;
  153.     }
  154.     recv_tcp(tcb,&bp,cnt);
  155.     /* Assemble an input line in the session buffer.
  156.      * Return if incomplete
  157.      */
  158.     while(pullup(&bp,&c,1) == 1){
  159.         switch(c){
  160.         case '\r':    /* Strip cr's */
  161. #ifdef MSDOS
  162.         case '\032':    /* Strip ctrl/Z's */
  163. #endif
  164.             continue;
  165.         default:    /* Assemble line */
  166.             if(mp->cnt != LINELEN-1){
  167.                 mp->buf[mp->cnt++] = c;
  168.                 break;
  169.             } /* break, but do not truncate long lines */
  170.         case '\n':    /* Complete line; process it */
  171.             mp->buf[mp->cnt] = '\0';
  172.             doline(mp);
  173.             break;
  174.         }
  175.     }
  176. }
  177. /* Process a line read on an SMTP connection (any state) */
  178. static void
  179. doline(mp)
  180. register struct mail *mp;
  181. {
  182.     void docommand(),deliver();
  183.  
  184.     switch(mp->state){
  185.     case COMMAND_STATE:
  186.         docommand(mp);
  187.         break;
  188.     case DATA_STATE:
  189.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  190.         if(mp->buf[0] == '.' && mp->buf[1] == '\0'){
  191.             mp->state = COMMAND_STATE;
  192.         /* Also sends appropriate response */
  193.             deliver(mp);
  194.             fclose(mp->data);
  195.             mp->data = NULLFILE;
  196.             del_list(mp->to);
  197.             mp->to = NULLLIST;
  198.             break;
  199.         }
  200.         /* for UNIX mail compatiblity */
  201.         if (strncmp(mp->buf,"From ",5) == 0)
  202.             putc('>',mp->data);
  203.         /* Append to data file */
  204.         fprintf(mp->data,"%s\n",mp->buf);
  205.         if(ferror(mp->data)){
  206.             mp->state = COMMAND_STATE;
  207.             tprintf(mp->tcb,ioerr);
  208.         }
  209.         break;
  210.     }
  211.     mp->cnt = 0;
  212. }
  213. /* Create control block, initialize */
  214. static struct mail *
  215. mail_create(tcb)
  216. register struct tcb *tcb;
  217. {
  218.     register struct mail *mp;
  219.  
  220.     if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL)
  221.         return NULLMAIL;
  222.     mp->tcb = tcb;        /* Downward pointer */
  223.     tcb->user = (char *)mp; /* Upward pointer */
  224.     return mp;
  225. }
  226.  
  227. /* Free resources, delete control block */
  228. static void
  229. mail_delete(mp)
  230. register struct mail *mp;
  231. {
  232.  
  233.     if (mp == NULLMAIL)
  234.         return;
  235.     if(mp->system != NULLCHAR)
  236.         free(mp->system);
  237.     if(mp->from != NULLCHAR)
  238.         free(mp->from);
  239.     if(mp->data != NULLFILE)
  240.         fclose(mp->data);
  241.     del_list(mp->to);
  242.     free((char *)mp);
  243. }
  244.  
  245. /* Parse and execute mail commands */
  246. static void
  247. docommand(mp)
  248. register struct mail *mp;
  249. {
  250.     register char **cmdp,*arg,*cp,*cmd;
  251.     FILE *tmpfile();
  252.     long t;
  253.     char address_type;
  254.     struct list *l;
  255.  
  256.     cmd = mp->buf;
  257.     if(mp->cnt < 4){
  258.         /* Can't be a legal SMTP command */
  259.         tprintf(mp->tcb,badcmd);
  260.         return;
  261.     }
  262.     cmd = mp->buf;
  263.  
  264.     /* Translate entire buffer to lower case */
  265.     for(cp = cmd;*cp != '\0';cp++)
  266.         *cp = tolower(*cp);
  267.  
  268.     /* Find command in table; if not present, return syntax error */
  269.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  270.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  271.             break;
  272.     if(*cmdp == NULLCHAR){
  273.         tprintf(mp->tcb,badcmd);
  274.         return;
  275.     }
  276.     arg = &cmd[strlen(*cmdp)];
  277.     /* Skip spaces after command */
  278.     while(*arg == ' ')
  279.         arg++;
  280.     stop_timer(&mp->tcb->timeout);    /* we received a good command! */
  281.     /* Execute specific command */
  282.     switch((int) (cmdp-commands)){
  283.     case HELO_CMD:
  284.         if(mp->system != NULLCHAR)
  285.             free(mp->system);
  286.         if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  287.             /* If the system is out of memory, just close */
  288.             close_tcp(mp->tcb);
  289.             break;
  290.         }
  291.         strcpy(mp->system,arg);
  292.         tprintf(mp->tcb,ourname,hostname);
  293.         break;
  294.     case NOOP_CMD:
  295.         tprintf(mp->tcb,ok);
  296.         break;
  297.     case MAIL_CMD:
  298.         if(mp->from != NULLCHAR)
  299.             free(mp->from);
  300.         if((mp->from = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  301.             /* If the system is out of memory, just close */
  302.             close_tcp(mp->tcb);
  303.             break;
  304.         }
  305.         if((cp = getname(arg)) == NULLCHAR){
  306.             tprintf(mp->tcb,syntax);
  307.             break;
  308.         }
  309.         strcpy(mp->from,cp);
  310.         tprintf(mp->tcb,ok);
  311.         break;
  312.     case QUIT_CMD:
  313.         tprintf(mp->tcb,closing);
  314.         close_tcp(mp->tcb);
  315.         break;
  316.     case RCPT_CMD:    /* Specify recipient */
  317.         if((cp = getname(arg)) == NULLCHAR){
  318.             tprintf(mp->tcb,syntax);
  319.             break;
  320.         }
  321.  
  322.         /* check if address is ok */
  323.         if ((address_type = validate_address(cp)) == BADADDR) {
  324.             tprintf(mp->tcb,unknown,cp);
  325.             break;
  326.         }
  327.         /* if a local address check for an alias */
  328.         if (address_type == LOCAL)
  329.             l = expandalias(&mp->to,cp);
  330.         else
  331.             /* a remote address is added to the list */
  332.             l = addlist(&mp->to,cp,address_type);
  333.  
  334.         if (l == NULLLIST)
  335.             tprintf(mp->tcb,rcpterr);
  336.         else
  337.             tprintf(mp->tcb,ok);
  338.         break;
  339.     case HELP_CMD:
  340.         tprintf(mp->tcb,help);
  341.         break;
  342.     case DATA_CMD:
  343.         if(mp->to == NULLLIST){
  344.             tprintf(mp->tcb,needrcpt);
  345.             break;
  346.         }
  347.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  348.         if((mp->data = tmpfile()) == NULLFILE){
  349.             tprintf(mp->tcb,ioerr);
  350.             break;
  351.         }
  352.         /* Add timestamp; ptime adds newline */
  353.         time(&t);
  354.         fprintf(mp->data,"Received: ");
  355.         if(mp->system != NULLCHAR)
  356.             fprintf(mp->data,"from %s "